package api

import (
	"encoding/json"
	"pan/common/conf"
	"pan/common/exception"
	"pan/common/log"
	"pan/common/net"
	"pan/common/util"
	"strings"
)

type DirRequest struct {
	Dir   string
	Owner string
	By    string
	Sort  string
}

type DirListRequest struct {
	Docid string `json:"docid"`
	By    string `json:"by"`
	Sort  string `json:"sort"`
	Attr  bool   `json:"attr"`
}

type DirListResponse struct {
	FailRsponse

	Dirs  []DirInfo  `json:"dirs"`
	Files []FileInfo `json:"files"`
}

type DirInfo struct {
	Docid       string `json:"docid"`
	Name        string `json:"name"`
	Rev         string `json:"rev"`
	Size        int64  `json:"size"`
	CreateTime  int64  `json:"create_time"`
	Creator     string `json:"creator"`
	Modified    int64  `json:"modified"`
	Editor      string `json:"editor"`
	CsfLevel    int64  `json:"csflevel"`
	DueDate     int64  `json:"duedate"`
	ClientMtime int64  `json:"client_mtime"`
	Attr        int64  `json:"attr"`
}

type FileInfo struct {
	Docid       string `json:"docid"`
	Name        string `json:"name"`
	Rev         string `json:"rev"`
	Size        int64  `json:"size"`
	CreateTime  int64  `json:"create_time"`
	Creator     string `json:"creator"`
	Modified    int64  `json:"modified"`
	Editor      string `json:"editor"`
	CsfLevel    int64  `json:"csflevel"`
	DueDate     int64  `json:"duedate"`
	ClientMtime int64  `json:"client_mtime"`
	Attr        int64  `json:"attr"`
}

func DirListApi(tokenid string, req *DirListRequest) (*DirListResponse, error) {
	url := util.PathJoin(conf.GlobalConfig.Url, conf.GlobalConfig.Paths.DirList)

	if log.IsDebugEnable() {
		bytes, _ := json.Marshal(req)
		log.Debug("dir list post: ", url, ", token:", tokenid, ", body:", string(bytes))
	}

	resp := &DirListResponse{}
	if err := net.PostWithToken(url, tokenid, req, resp); err != nil {
		return nil, err
	}

	if log.IsDebugEnable() {
		result, _ := json.Marshal(resp)
		log.Debug("dir list response: ", string(result))
	}
	if resp.Errcode != 0 {
		return nil, exception.NewExpectError("dir list failed, %s", resp.FailString())
	}
	return resp, nil
}

// DirApi 类似cd命令一样查询目录内容
func DirApi(tokenid string, req *DirRequest) (*DirListResponse, error) {
	if req.By == "" {
		req.By = "time"
		req.Sort = "desc"
	}
	search := SearchDir{Tokenid: tokenid, Request: req}
	return search.Dir()
}

type SearchDir struct {
	Tokenid string
	Request *DirRequest
}

func (s *SearchDir) Dir() (*DirListResponse, error) {
	dirs := strings.Split(s.Request.Dir, "/")
	if dirs == nil || len(dirs) == 0 {
		return nil, exception.NewExpectError("doc dir must be not empty")
	}

	userName := s.Request.Owner
	if s.Request.Owner == "" {
		var err error
		if userName, err = s.findUserName(); err != nil {
			return nil, err
		}
	}

	rootDirDocid, err := s.findRootDirDocid(dirs[0], userName)
	if err != nil {
		return nil, err
	}

	return s.findInDirs(dirs[1:], rootDirDocid)
}

func (s *SearchDir) LastDirDocid() (string, error) {
	dirs := strings.Split(s.Request.Dir, "/")
	if dirs == nil || len(dirs) == 0 {
		return "", exception.NewExpectError("doc dir must be not empty")
	}

	userName := s.Request.Owner
	if s.Request.Owner == "" {
		var err error
		if userName, err = s.findUserName(); err != nil {
			return "", err
		}
	}

	rootDirDocid, err := s.findRootDirDocid(dirs[0], userName)
	if err != nil {
		return "", err
	}
	return s.findLastDirDocid(dirs[1:], rootDirDocid)
}

func (s *SearchDir) findUserName() (string, error) {
	rsp, err := UserApi(s.Tokenid)
	if err != nil {
		return "", err
	}
	return rsp.Name, nil
}

func (s *SearchDir) findRootDirDocid(typeName, userName string) (string, error) {
	rsp, err := DocsApi(s.Tokenid, &DocsRequest{DocType: 0})
	if err != nil {
		return "", err
	}
	for _, doc := range rsp.DocInfos {
		if typeName == doc.TypeName && userName == doc.Creator {
			return doc.Docid, nil
		}
	}
	return "", exception.NewExpectError("not found anything in %s(%s)", typeName, userName)
}

// 返回3中情况
// "",!nil: 失败,存在错误
// "",nil: 成功,但是未查询到任何结果
// !"",nil: 成功,查询到docid
func (s *SearchDir) findLastDirDocid(dirs []string, rootDirDocid string) (string, error) {
	if dirs == nil || len(dirs) == 0 {
		return rootDirDocid, nil
	}

	parentDocid := rootDirDocid
	for _, dirName := range dirs {
		if dirName == "" {
			continue
		}
		docid, err := s.getDirDocid(dirName, parentDocid)
		if err != nil {
			return "", err
		}
		if docid == "" {
			return "", nil
		}
		parentDocid = docid
	}
	return parentDocid, nil
}

func (s *SearchDir) findInDirs(dirs []string, rootDirDocid string) (*DirListResponse, error) {
	docid, err := s.findLastDirDocid(dirs, rootDirDocid)
	if err != nil {
		return nil, err
	}
	if docid == "" {
		return &DirListResponse{}, nil
	}
	rsp, err := DirListApi(s.Tokenid, &DirListRequest{Docid: docid, By: s.Request.By, Sort: s.Request.Sort})
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

// 返回3中情况
// "",!nil: 失败,存在错误
// "",nil: 成功,但是未查询到任何结果
// !"",nil: 成功,查询到docid
func (s *SearchDir) getDirDocid(dirName string, parentDocid string) (string, error) {
	rsp, err := DirListApi(s.Tokenid, &DirListRequest{Docid: parentDocid, By: s.Request.By, Sort: s.Request.Sort})
	if err != nil {
		return "", err
	}

	if (rsp.Dirs == nil || len(rsp.Dirs) == 0) && (rsp.Files == nil || len(rsp.Files) == 0) {
		return "", nil
	}
	for _, dir := range rsp.Dirs {
		if dirName == dir.Name {
			return dir.Docid, nil
		}
	}
	return "", nil
}
